package com.magnificent.panda.utils;

import com.magnificent.panda.model.entity.FlowEvent;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYBarPainter;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYIntervalSeries;
import org.jfree.data.xy.XYIntervalSeriesCollection;
import org.jfree.ui.GradientPaintTransformer;
import org.jfree.ui.RectangleEdge;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

/**
 * User: Babanin
 * Date: 06.03.2010
 * Time: 9:03:57
 */
public final class JFreeChartHelper {
    public static JFreeChart newFlowChart(XYDataset dataset, String title, String axisX, String axisY, int maxRangeValue) {
        JFreeChart chart = ChartFactory.createXYLineChart(
                title, // chart title
                axisX, // x axis label
                axisY, // y axis label
                dataset, // data
                PlotOrientation.VERTICAL,
                false, // include legend
                true, // tooltips
                false // urls
        );
        chart.setBackgroundPaint(Color.WHITE);

        XYPlot localXYPlot = chart.getXYPlot();
        localXYPlot.setBackgroundPaint(Color.WHITE);

        // NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
        NumberAxis localNumberAxis2 = new NumberAxis("");
        localNumberAxis2.setUpperMargin(0.0D);
        localXYPlot.setRangeAxis(0, localNumberAxis2);
        localXYPlot.mapDatasetToRangeAxis(1, 1);

        NumberAxis localNumberAxis1 = new NumberAxis("Время (мин)");
//        localNumberAxis1.setAutoRange(false);
//        localNumberAxis1.setAutoRangeMinimumSize(10.0D);
//        localNumberAxis1.setAutoRangeMinimumSize(10.0D);
//        localNumberAxis1.setUpperMargin(50.0D);
        localNumberAxis1.setRange(0, maxRangeValue);
//        localNumberAxis1.setFixedAutoRange(100.0D);
        localXYPlot.setDomainAxis(0, localNumberAxis1);
//        localXYPlot.mapDatasetToRangeAxis(1, 1);

//        NumberAxis localNumberAxis1 = new NumberAxis("???");
//        localNumberAxis2.setUpperMargin(2.0D);
//        localXYPlot.setRangeAxis(1, localNumberAxis1);
//        localXYPlot.mapDatasetToRangeAxis(0, 1);

        XYBarRenderer localXYBarRenderer = new XYBarRenderer(0.5D);
        localXYBarRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0,000.00")));
        localXYPlot.setRenderer(0, localXYBarRenderer);
        localXYBarRenderer.setBarPainter(new FlowXYPainter());
        localXYBarRenderer.setShadowVisible(false);

        return chart;
    }

    public static JFreeChart newCorrelationChart(XYDataset dataset, String title, String axisX, String axisY) {
        JFreeChart chart = ChartFactory.createXYLineChart(
                title, // chart title
                axisX, // x axis label
                axisY, // y axis label
                dataset, // data
                PlotOrientation.VERTICAL,
                false, // include legend
                true, // tooltips
                false // urls
        );
        chart.setBackgroundPaint(Color.WHITE);

        XYPlot localXYPlot = chart.getXYPlot();
        localXYPlot.setBackgroundPaint(Color.WHITE);

        // NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
        NumberAxis localNumberAxis2 = new NumberAxis("");
        localNumberAxis2.setUpperMargin(1.0D);
        localXYPlot.setRangeAxis(0, localNumberAxis2);
        localXYPlot.setRangeAxis(0, localNumberAxis2);
        localXYPlot.mapDatasetToRangeAxis(1, 1);

        XYBarRenderer localXYBarRenderer = new XYBarRenderer(0.5D);
        localXYBarRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0,000.00")));
        localXYPlot.setRenderer(0, localXYBarRenderer);
        localXYBarRenderer.setBarPainter(new CorrelationPainter());
        localXYBarRenderer.setShadowVisible(false);

        return chart;
    }

    public static IntervalXYDataset createDataset(double corr[], double corrMixed[]) {
        XYIntervalSeriesCollection dd = new XYIntervalSeriesCollection();

        XYIntervalSeries series = new XYIntervalSeries(new Comparable() {
            public int compareTo(Object o) {
                return 0;
            }
        });
        series.setDescription("");

        for (int h = 0; h < corrMixed.length; h++) {
            series.add(-h, -h - 0.1, -h + 0.1, corrMixed[h], corrMixed[h], corrMixed[h]);
        }

        for (int h = 1; h < corr.length; h++) {
            series.add(h, h - 0.1, h + 0.1, corr[h], corr[h], corr[h]);
        }


        dd.addSeries(series);
        return dd;
    }

    public static XYDataset createFlowDataset(java.util.List<FlowEvent> flow) {
        XYIntervalSeriesCollection dd = new XYIntervalSeriesCollection();

        XYIntervalSeries series = new XYIntervalSeries(new Comparable() {
            public int compareTo(Object o) {
                return 0;
            }
        });
        series.setDescription("");

        for (int h = 0; h < flow.size(); h++) {
            series.add(1, flow.get(h).getValue() - 0.1, flow.get(h).getValue() + 0.1, 1, 1, 1);
        }


        dd.addSeries(series);
        return dd;
    }


    public static XYDataset createFlowDataset(int[] flow) {
        XYIntervalSeriesCollection dd = new XYIntervalSeriesCollection();

        XYIntervalSeries series = new XYIntervalSeries(new Comparable() {
            public int compareTo(Object o) {
                return 0;
            }
        });
        series.setDescription("");

        for (int h = 0; h < flow.length; h++) {
            series.add(1, flow[h] - 0.1, flow[h] + 0.1, 1, 1, 1);
        }

        dd.addSeries(series);
        return dd;
    }

    public static class CorrelationPainter implements XYBarPainter, Serializable {
        public CorrelationPainter() {
        }

        public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row,
                             int column, RectangularShape bar, RectangleEdge base) {

            Paint itemPaint = renderer.getItemPaint(row, column);
            GradientPaintTransformer t = renderer.getGradientPaintTransformer();
            if (t != null && itemPaint instanceof GradientPaint) {
                itemPaint = t.transform((GradientPaint) itemPaint, bar);
            }
            g2.setPaint(itemPaint);
            g2.fill(bar);

            double x = bar.getX();
            double y = bar.getY();

            RectangularShape shape = new Rectangle2D.Double(x - 5, y, 12, 1);

            g2.fill(shape);

            // draw the outline...
            /* if (renderer.isDrawBarOutline()) {
                // && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
                Stroke stroke = renderer.getItemOutlineStroke(row, column);
                Paint paint = renderer.getItemOutlinePaint(row, column);
                if (stroke != null && paint != null) {
                    g2.setStroke(stroke);
                    g2.setPaint(paint);
                    g2.draw(bar);
                }
            }*/

        }

        /**
         * Paints a single bar instance.
         *
         * @param g2        the graphics target.
         * @param renderer  the renderer.
         * @param row       the row index.
         * @param column    the column index.
         * @param bar       the bar
         * @param base      indicates which side of the rectangle is the base of the
         *                  bar.
         * @param pegShadow peg the shadow to the base of the bar?
         */
        public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row,
                                   int column, RectangularShape bar, RectangleEdge base,
                                   boolean pegShadow) {

            // handle a special case - if the bar colour has alpha == 0, it is
            // invisible so we shouldn't draw any shadow
            Paint itemPaint = renderer.getItemPaint(row, column);
            if (itemPaint instanceof Color) {
                Color c = (Color) itemPaint;
                if (c.getAlpha() == 0) {
                    return;
                }
            }

            RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(),
                    renderer.getShadowYOffset(), base, pegShadow);
            g2.setPaint(Color.gray);
            g2.fill(shadow);

        }

        /**
         * Creates a shadow for the bar.
         *
         * @param bar       the bar shape.
         * @param xOffset   the x-offset for the shadow.
         * @param yOffset   the y-offset for the shadow.
         * @param base      the edge that is the base of the bar.
         * @param pegShadow peg the shadow to the base?
         * @return A rectangle for the shadow.
         */
        private Rectangle2D createShadow(RectangularShape bar, double xOffset,
                                         double yOffset, RectangleEdge base, boolean pegShadow) {
            double x0 = bar.getMinX();
            double x1 = bar.getMaxX();
            double y0 = bar.getMinY();
            double y1 = bar.getMaxY();
            if (base == RectangleEdge.TOP) {
                x0 += xOffset;
                x1 += xOffset;
                if (!pegShadow) {
                    y0 += yOffset;
                }
                y1 += yOffset;
            } else if (base == RectangleEdge.BOTTOM) {
                x0 += xOffset;
                x1 += xOffset;
                y0 += yOffset;
                if (!pegShadow) {
                    y1 += yOffset;
                }
            } else if (base == RectangleEdge.LEFT) {
                if (!pegShadow) {
                    x0 += xOffset;
                }
                x1 += xOffset;
                y0 += yOffset;
                y1 += yOffset;
            } else if (base == RectangleEdge.RIGHT) {
                x0 += xOffset;
                if (!pegShadow) {
                    x1 += xOffset;
                }
                y0 += yOffset;
                y1 += yOffset;
            }
            return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
        }

        /**
         * Tests this instance for equality with an arbitrary object.
         *
         * @param obj the obj (<code>null</code> permitted).
         * @return A boolean.
         */
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            }
            if (!(obj instanceof StandardXYBarPainter)) {
                return false;
            }
            return true;
        }

        /**
         * Returns a hash code for this instance.
         *
         * @return A hash code.
         */
        public int hashCode() {
            int hash = 37;
            // no fields to compute...
            return hash;
        }
    }

    public static class FlowXYPainter implements XYBarPainter, Serializable {
        public FlowXYPainter() {
        }

        /**
         * Paints a single bar instance.
         *
         * @param g2       the graphics target.
         * @param renderer the renderer.
         * @param row      the row index.
         * @param column   the column index.
         * @param bar      the bar
         * @param base     indicates which side of the rectangle is the base of the
         *                 bar.
         */
        public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row,
                             int column, RectangularShape bar, RectangleEdge base) {

            Paint itemPaint = renderer.getItemPaint(row, column);
            GradientPaintTransformer t = renderer.getGradientPaintTransformer();
            if (t != null && itemPaint instanceof GradientPaint) {
                itemPaint = t.transform((GradientPaint) itemPaint, bar);
            }
            g2.setPaint(itemPaint);
            g2.fill(bar);

            double x = bar.getX();
            double y = bar.getY();

            RectangularShape shape = new Rectangle2D.Double(x - 5, y, 12, 1);

            g2.fill(shape);

            // draw the outline...
            /* if (renderer.isDrawBarOutline()) {
                // && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
                Stroke stroke = renderer.getItemOutlineStroke(row, column);
                Paint paint = renderer.getItemOutlinePaint(row, column);
                if (stroke != null && paint != null) {
                    g2.setStroke(stroke);
                    g2.setPaint(paint);
                    g2.draw(bar);
                }
            }*/

        }

        /**
         * Paints a single bar instance.
         *
         * @param g2        the graphics target.
         * @param renderer  the renderer.
         * @param row       the row index.
         * @param column    the column index.
         * @param bar       the bar
         * @param base      indicates which side of the rectangle is the base of the
         *                  bar.
         * @param pegShadow peg the shadow to the base of the bar?
         */
        public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row,
                                   int column, RectangularShape bar, RectangleEdge base,
                                   boolean pegShadow) {

            // handle a special case - if the bar colour has alpha == 0, it is
            // invisible so we shouldn't draw any shadow
            Paint itemPaint = renderer.getItemPaint(row, column);
            if (itemPaint instanceof Color) {
                Color c = (Color) itemPaint;
                if (c.getAlpha() == 0) {
                    return;
                }
            }

            RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(),
                    renderer.getShadowYOffset(), base, pegShadow);
            g2.setPaint(Color.gray);
            g2.fill(shadow);

        }

        /**
         * Creates a shadow for the bar.
         *
         * @param bar       the bar shape.
         * @param xOffset   the x-offset for the shadow.
         * @param yOffset   the y-offset for the shadow.
         * @param base      the edge that is the base of the bar.
         * @param pegShadow peg the shadow to the base?
         * @return A rectangle for the shadow.
         */
        private Rectangle2D createShadow(RectangularShape bar, double xOffset,
                                         double yOffset, RectangleEdge base, boolean pegShadow) {
            double x0 = bar.getMinX();
            double x1 = bar.getMaxX();
            double y0 = bar.getMinY();
            double y1 = bar.getMaxY();
            if (base == RectangleEdge.TOP) {
                x0 += xOffset;
                x1 += xOffset;
                if (!pegShadow) {
                    y0 += yOffset;
                }
                y1 += yOffset;
            } else if (base == RectangleEdge.BOTTOM) {
                x0 += xOffset;
                x1 += xOffset;
                y0 += yOffset;
                if (!pegShadow) {
                    y1 += yOffset;
                }
            } else if (base == RectangleEdge.LEFT) {
                if (!pegShadow) {
                    x0 += xOffset;
                }
                x1 += xOffset;
                y0 += yOffset;
                y1 += yOffset;
            } else if (base == RectangleEdge.RIGHT) {
                x0 += xOffset;
                if (!pegShadow) {
                    x1 += xOffset;
                }
                y0 += yOffset;
                y1 += yOffset;
            }
            return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
        }

        /**
         * Tests this instance for equality with an arbitrary object.
         *
         * @param obj the obj (<code>null</code> permitted).
         * @return A boolean.
         */
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            }
            if (!(obj instanceof StandardXYBarPainter)) {
                return false;
            }
            return true;
        }

        /**
         * Returns a hash code for this instance.
         *
         * @return A hash code.
         */
        public int hashCode() {
            int hash = 37;
            // no fields to compute...
            return hash;
        }
    }
}

  /* converted to utf8 */